package cn.zyl.common.redis.util;

import cn.zyl.common.redis.exception.MiniRedisDataException;
import cn.zyl.common.redis.exception.MiniRedisException;
import cn.zyl.common.redis.exception.MiniRedisRedirectionException;
import redis.clients.jedis.exceptions.JedisConnectionException;

import java.io.ByteArrayOutputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * MiniRedisInputStream 类说明: 这个类假定（在某种程度上）我们正在读取RESP流。因此，它假定有关CRLF线路终端的某些约定。
 * 它还假设，如果协议层需要一个字节，那么如果没有这个字节，那么就是一个流错误
 * @author wsz
 * @version v1.0
 * @date 2020/8/7
 */
public class MiniRedisInputStream extends FilterInputStream {

    protected final byte[] buf;
    protected int count, limit;
    private final static int SIZE = 8192;
    private final static int N_0 = 0;
    private final static int N_10 = 10;
    private final static int NE_1 = -1;
    private final static int N_2 = 2;
    private final static int N_16 = 16;
    private final static byte C_R = '\r';
    private final static byte C_N = '\n';
    private final static byte C_0 = '0';
    private final static byte C__ = '-';
    public MiniRedisInputStream(InputStream in, int size) {
        super(in);
        if (size <= N_0) {
            throw new IllegalArgumentException("Buffer size <= 0");
        }
        buf = new byte[size];
    }
    public MiniRedisInputStream(InputStream in) {
        this(in, SIZE);
    }
    public byte readByte() throws JedisConnectionException {
        ensureFill();
        return buf[count++];
    }

    public String readLine() {
        final StringBuilder sb = new StringBuilder();
        while (true) {
            ensureFill();
            byte b = buf[count++];
            if (b == C_R) {
                ensureFill();
                byte c = buf[count++];
                if (c == C_N) {
                    break;
                }
                sb.append((char) b);
                sb.append((char) c);
            } else {
                sb.append((char) b);
            }
        }
        final String reply = sb.toString();
        if (reply.length() == N_0) {
            throw new JedisConnectionException("It seems like server has closed the connection.");
        }
        return reply;
    }
    /**
     * 此操作只需要一次加注。在这种典型的情况下，我们优化字节数组的分配和复制。
     * 在需要多次填充的边缘情况下，我们采用较慢的路径并根据需要扩展字节数组输出流。
     */
    public byte[] readLineBytes() {
        ensureFill();
        int pos = count;
        final byte[] buf = this.buf;
        while (true) {
            if (pos == limit) {
                return readLineBytesSlowly();
            }
            if (buf[pos++] == C_R) {
                if (pos == limit) {
                    return readLineBytesSlowly();
                }
                if (buf[pos++] == C_N) {
                    break;
                }
            }
        }

        final int N = (pos - count) - N_2;
        final byte[] line = new byte[N];
        System.arraycopy(buf, count, line, N_0, N);
        count = pos;
        return line;
    }

    /**
     * 慢速路径，以防在一次#fill（）操作中无法读取一行字节。
     * 这仍然比创建string builder字符串，然后在协议中编码为byte[]然后解码回字符串更快
     */
    private byte[] readLineBytesSlowly() {
        ByteArrayOutputStream bout = null;
        while (true) {
            ensureFill();

            byte b = buf[count++];
            if (b == C_R) {
                //必须多出一个字节
                ensureFill();
                byte c = buf[count++];
                if (c == C_N) {
                    break;
                }
                if (bout == null) {
                    bout = new ByteArrayOutputStream(N_16);
                }
                bout.write(b);
                bout.write(c);
            } else {
                if (bout == null) {
                    bout = new ByteArrayOutputStream(N_16);
                }
                bout.write(b);
            }
        }

        return bout == null ? new byte[N_0] : bout.toByteArray();
    }

    public int readIntCrLf() {
        return (int) readLongCrLf();
    }

    public long readLongCrLf() {
        byte[] buf = this.buf;
        ensureFill();
        final boolean isNeg = buf[count] == C__;
        if (isNeg) {
            ++count;
        }
        long value = N_0;
        while (true) {
            ensureFill();
            final int b = buf[count++];
            if (b == C_R) {
                ensureFill();
                if (buf[count++] != C_N) {
                    throw new JedisConnectionException("Unexpected character!");
                }
                break;
            } else {
                value = value * N_10 + b - C_0;
            }
        }
        return (isNeg ? -value : value);
    }

    @Override
    public int read(byte[] b, int off, int len) throws JedisConnectionException {
        ensureFill();

        final int length = Math.min(limit - count, len);
        System.arraycopy(buf, count, b, off, length);
        count += length;
        return length;
    }

    /**
     * 此方法假定需要读取所需的字节。如果我们不能再读取字节，就会抛出一个异常，以快速确定流比预期的要小
     */
    private void ensureFill() throws JedisConnectionException {
        if (count >= limit) {
            try {
                limit = in.read(buf);
                count = N_0;
                if (limit == NE_1) {
                    throw new JedisConnectionException("Unexpected end of stream.");
                }
            } catch (IOException e) {
                throw new JedisConnectionException(e);
            }
        }
    }
}
